﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Collections;
using System.Diagnostics;

namespace TwainLib
{
    public class DeviceSourceCapability
    {
        public DeviceSourceCapability(DeviceSource ds, TwCap cap, TwOn conType = TwOn.DontCare)
        {
            capability = new TwCapability();
            capability.Cap = cap;
            capability.ConType = conType;
            capability.Handle = IntPtr.Zero;
            deviceSource = ds;
        }

        public TwOnType FromHandle()
        {
            if (capability.Handle != IntPtr.Zero)
            {
                switch (capability.ConType)
                {
                    case TwOn.One:
                        twOnValue = new TwOneValue();
                        break;
                    case TwOn.Array:
                        twOnValue = new TwArray();
                        break;
                    case TwOn.Enum:
                        twOnValue = new TwEnumeration();
                        break;
                    case TwOn.Range:
                        twOnValue = new TwRange();
                        break;
                }
                if (twOnValue != null)
                {
                    twOnValue.PtrToStructure(capability.Handle, deviceSource.DeviceSourceManager.EntryPoint);
                    deviceSource.DeviceSourceManager.EntryPoint.TwMemUnlock(capability.Handle);
                }
                deviceSource.DeviceSourceManager.EntryPoint.TwMemFree(capability.Handle);
                capability.Handle = IntPtr.Zero;
            }
            return twOnValue;
        }

        public void GenerateHelpText()
        {
            TwOneValue helpValue = new TwOneValue();
            helpValue.PtrToStructure(capability.Handle, deviceSource.DeviceSourceManager.EntryPoint);
            Debug.WriteLine(helpValue.Item);
        }

        public void GenerateLabelText()
        {
            TwOneValue helpValue = new TwOneValue();
            helpValue.PtrToStructure(capability.Handle, deviceSource.DeviceSourceManager.EntryPoint);
            Debug.WriteLine(helpValue.Item);
        }

        public bool SetCapability()
        {
            twOnValue.StructureToPtr(capability, deviceSource.DeviceSourceManager.EntryPoint);
            deviceSource.SetCapability(this);
            return true;
        }

        public override string ToString()
        {
            return string.Format("Support: {0} , Name: {1}, TwOnType: {2}, Value: {3}", 
                canSupported, capability.Cap.ToString(), capability.ConType.ToString(), twOnValue);
        }

        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        [DataContractAttribute]
        public class TwCapability
        {
            [DataMemberAttribute]
            public TwCap Cap;
            [DataMemberAttribute]
            public TwOn ConType;
            [DataMemberAttribute]
            public IntPtr Handle;
        }

        public interface TwOnType
        {
            void PtrToStructure(IntPtr handle, TwEntryPointMethods methods);
            void StructureToPtr(TwCapability cap, TwEntryPointMethods methods);
        }

        [StructLayout(LayoutKind.Sequential, Pack=2)]
        [DataContractAttribute]
        public class TwOneValue : TwOnType
        {
            [DataMemberAttribute]
            public TwType ItemType;
            [DataMemberAttribute]
            public object Item;

            public override string ToString()
            {
                return string.Format("[ItemType:{0}, Item:{1}]", ItemType.ToString(), Item);
            }

            public void PtrToStructure(IntPtr handle, TwEntryPointMethods methods)
            {
                IntPtr pv = methods.TwMemLock(handle);
                ItemType = (TwType)Marshal.ReadInt16(pv, 0);
                IntPtr value = Marshal.ReadIntPtr(pv, 2);
                methods.TwMemUnlock(handle);

                Item = TwUtils.PtrToTwType(ItemType, value, methods);
            }


            public void StructureToPtr(TwCapability cap, TwEntryPointMethods methods)
            {
                Debug.Assert(cap != null);

                cap.Handle = methods.TwMemAllocate(6);
                IntPtr pv = methods.TwMemLock(cap.Handle);
                Marshal.WriteInt16(pv, 0, (short)ItemType);
                TwUtils.WriteToMem(pv, 2, ItemType, Item);
                methods.TwMemUnlock(cap.Handle);
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        [DataContractAttribute]
        public class TwArray : TwOnType
        {
            [DataMemberAttribute]
            public TwType ItemType;
            [DataMemberAttribute]
            public int NumItems = 0;
            [DataMemberAttribute]
            public ArrayList ItemList;

            public override string ToString()
            {
                return string.Format("[ItemType:{0}, NumItems:{1}, ItemList:{2}]", ItemType.ToString(), 
                    NumItems, String.Join(",", ItemList.ToArray()));
            }

            public void PtrToStructure(IntPtr handle, TwEntryPointMethods methods)
            {
                IntPtr pv = methods.TwMemLock(handle);
                ItemType = (TwType)Marshal.ReadInt16(pv, 0);
                NumItems = Marshal.ReadInt32(pv, 2);
                if (NumItems > 0)
                    ItemList = new ArrayList();
                for (int i = 0; i < NumItems; i++)
                {
                    ItemList.Add(TwUtils.ReadFromMem(pv, 6 + i * TwUtils.GetTwTypeSize(ItemType), ItemType));
                }
                methods.TwMemUnlock(handle);
            }


            public void StructureToPtr(TwCapability cap, TwEntryPointMethods methods)
            {
                int itemSize = TwUtils.GetTwTypeSize(ItemType);
                int memSize = 6 + itemSize * NumItems;
                cap.Handle = methods.TwMemAllocate(memSize);
                IntPtr pv = methods.TwMemLock(cap.Handle);
                Marshal.WriteInt16(pv, 0, (short)ItemType);
                Marshal.WriteInt32(pv, 2, NumItems);
                for (int i = 0; i < NumItems; i++)
                {
                    TwUtils.WriteToMem(pv, 6 + itemSize * i, ItemType, ItemList[i]);
                }
                methods.TwMemUnlock(cap.Handle);
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        [DataContractAttribute]
        public class TwEnumeration : TwOnType
        {
            [DataMemberAttribute]
            public TwType ItemType;
            [DataMemberAttribute]
            public int NumItems = 0;
            [DataMemberAttribute]
            public int CurrentIndex = 0;
            [DataMemberAttribute]
            public int DefaultIndex = 0;
            [DataMemberAttribute]
            public ArrayList ItemList;

            public override string ToString()
            {
                return string.Format("[ItemType:{0}, NumItems:{1}, CurrentIndex:{2}, DefaultIndex:{3}, ItemList:{4}]", ItemType.ToString(),
                    NumItems, CurrentIndex, DefaultIndex, String.Join(",", ItemList.ToArray()));
            }

            public void PtrToStructure(IntPtr handle, TwEntryPointMethods methods)
            {
                IntPtr pv = methods.TwMemLock(handle);
                ItemType = (TwType)Marshal.ReadInt16(pv, 0);
                NumItems = Marshal.ReadInt32(pv, 2);
                CurrentIndex = Marshal.ReadInt32(pv, 6);
                DefaultIndex = Marshal.ReadInt32(pv, 10);
                if (NumItems > 0)
                    ItemList = new ArrayList();
                for (int i = 0; i < NumItems; i++)
                {
                    ItemList.Add(TwUtils.ReadFromMem(pv, 14 + i * TwUtils.GetTwTypeSize(ItemType), ItemType));
                }
                methods.TwMemUnlock(handle);
            }


            public void StructureToPtr(TwCapability cap, TwEntryPointMethods methods)
            {
                int itemSize = TwUtils.GetTwTypeSize(ItemType);
                int memSize = 14 + itemSize * NumItems;
                cap.Handle = methods.TwMemAllocate(memSize);
                IntPtr pv = methods.TwMemLock(cap.Handle);
                Marshal.WriteInt16(pv, 0, (short)ItemType);
                Marshal.WriteInt32(pv, 2, NumItems);
                Marshal.WriteInt32(pv, 6, CurrentIndex);
                Marshal.WriteInt32(pv, 10, DefaultIndex);
                for (int i = 0; i < NumItems; i++)
                {
                    TwUtils.WriteToMem(pv, 6 + itemSize * i, ItemType, ItemList[i]);
                }
                methods.TwMemUnlock(cap.Handle);
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        [DataContractAttribute]
        public class TwRange : TwOnType
        {
            [DataMemberAttribute]
            public TwType ItemType;
            [DataMemberAttribute]
            public float MinValue;
            [DataMemberAttribute]
            public float MaxValue;
            [DataMemberAttribute]
            public float StepSize;
            [DataMemberAttribute]
            public float DefaultValue;
            [DataMemberAttribute]
            public float CurrentValue;

            public override string ToString()
            {
                return string.Format("[ItemType:{0}, MinValue:{1}, MaxValue:{2}, StepSize:{3}, DefaultValue:{4}, CurrentValue:{5}]", 
                    ItemType.ToString(), MinValue, MaxValue, StepSize, DefaultValue, CurrentValue);
            }

            public void PtrToStructure(IntPtr handle, TwEntryPointMethods methods)
            {
                IntPtr pv = methods.TwMemLock(handle);
                ItemType = (TwType)Marshal.ReadInt16(pv, 0);
                int itemSize = TwUtils.GetTwTypeSize(ItemType);
                MinValue = (float)TwUtils.ReadFromMem(pv, 2, ItemType);
                MaxValue = (float)TwUtils.ReadFromMem(pv, 2 + itemSize, ItemType);
                StepSize = (float)TwUtils.ReadFromMem(pv, 2 + itemSize * 2, ItemType);
                DefaultValue = (float)TwUtils.ReadFromMem(pv, 2 + itemSize * 3, ItemType);
                CurrentValue = (float)TwUtils.ReadFromMem(pv, 2 + itemSize * 4, ItemType);
                methods.TwMemUnlock(handle);
            }


            public void StructureToPtr(TwCapability cap, TwEntryPointMethods methods)
            {
                cap.Handle = methods.TwMemAllocate(22);
                IntPtr pv = methods.TwMemLock(cap.Handle);
                Marshal.WriteInt16(pv, (short)ItemType, 0);
                TwFix32 minValue = new TwFix32();
                minValue.FromFloat(MinValue);
                TwUtils.WriteToMem(pv, 2, TwType.Fix32, minValue);
                TwFix32 maxValue = new TwFix32();
                maxValue.FromFloat(MaxValue);
                TwUtils.WriteToMem(pv, 6, TwType.Fix32, maxValue);
                TwFix32 stepSize = new TwFix32();
                stepSize.FromFloat(StepSize);
                TwUtils.WriteToMem(pv, 10, TwType.Fix32, stepSize);
                TwFix32 defaultValue = new TwFix32();
                defaultValue.FromFloat(DefaultValue);
                TwUtils.WriteToMem(pv, 14, TwType.Fix32, defaultValue);
                TwFix32 currentValue = new TwFix32();
                currentValue.FromFloat(CurrentValue);
                TwUtils.WriteToMem(pv, 18, TwType.Fix32, currentValue);
                methods.TwMemUnlock(cap.Handle);
            }
        }

        protected TwCapability capability;
        public TwCapability Capability
        {
            get
            {
                return capability;
            }
        }

        protected TwOnType twOnValue;

        public TwOnType TwOnValue
        {
            get { return twOnValue; }
            set { twOnValue = value; }
        }

        protected DeviceSource deviceSource;
        public DeviceSource DeviceSource
        {
            get
            {
                return deviceSource;
            }
        }

        private bool write;
        protected bool Write
        {
            get { return write; }
        }

        protected bool canSupported = false;
        public bool CanSupported
        {
            get { return canSupported; }
            set { canSupported = value; }
        }

        protected bool tested;

        public bool Tested
        {
            get { return tested; }
        }

        protected string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private string description;

        public string Description
        {
            get { return description; }
            set { description = value; }
        }
    }
}
